/*
 * SPDX-FileCopyrightText: 2025 UnionTech Software Technology Co., Ltd.
 *
 * SPDX-License-Identifier: GPL-2.0-or-later
 */

#![allow(
    dead_code,
    mutable_transmutes,
    non_camel_case_types,
    non_snake_case,
    non_upper_case_globals,
    unused_assignments,
    unused_mut,
    static_mut_refs,
    unused_variables,
    improper_ctypes,
    clippy::manual_unwrap_or,
    clippy::manual_unwrap_or_default,
    clippy::match_like_matches_macro,
    clippy::collapsible_if,
    clippy::unwrap_or_default,
    clippy::redundant_pattern_matching,
    clippy::nonminimal_bool
)]

mod common;
mod libuser;
mod utconfig;
extern crate libc;

use clap::{Arg, Command};
use common::*;
use libc::passwd;
use libc::{c_int, c_void, getpwnam, getpwuid, time_t};
use libc::{EAFNOSUPPORT, EINVAL, EPROTONOSUPPORT};
use nix::unistd::getuid;
use std::ffi::{CStr, CString, NulError}; // For C-style strings in Rust
                                         // use std::fmt::Arguments;
use std::io::{self, Read};
use std::os::raw::c_char;
use std::path::Path;
use std::process::exit; // To exit with specific status
use std::ptr;
use std::ptr::addr_of;
use std::{env, process};

// Define constants
pub const PASSWD_KEEP: i32 = 0x0001;
pub const PASSWD_LOCK: i32 = 0x0002;
pub const PASSWD_UNLOCK: i32 = 0x0004;
pub const PASSWD_DELETE: i32 = 0x0008;
pub const PASSWD_STATUS: i32 = 0x0010;
pub const PASSWD_FORCE: i32 = 0x0020;
pub const PASSWD_STDIN: i32 = 0x0040;
pub const PASSWD_EXPIRE: i32 = 0x0080;
pub const PASSWD_ROOT: i32 = 0x009E;
pub const PASSWD_MIN: i32 = 0x0100;
pub const PASSWD_MAX: i32 = 0x0200;
pub const PASSWD_WARN: i32 = 0x0400;
pub const PASSWD_INACT: i32 = 0x0800;
pub const PASSWD_AGING: i32 = 0x0F00;

pub const MAX_USERNAMESIZE: usize = 32;
pub const PATH_MAX: c_int = 4096;
pub const SELINUX_CB_LOG: c_int = 0;
pub const AUDIT_USER_AVC: c_int = 1107;
pub const AUDIT_USER_CHAUTHTOK: c_int = 1108;
pub const AUDIT_ACCT_LOCK: c_int = 1135;
pub const AUDIT_ACCT_UNLOCK: c_int = 1136;
pub const AUDIT_USER_MGMT: c_int = 1102;
pub const STDIO_FILENO: c_int = 0;
pub const PAM_MAX_RESP_SIZE: usize = 512;
pub const PAM_TTY: c_int = 3;
pub const PAM_CONV_ERR: c_int = 19;
pub const PAM_SUCCESS: c_int = 0;
pub const PAM_PROMPT_ECHO_OFF: c_int = 1;
pub const PAM_CHANGE_EXPIRED_AUTHTOK: c_int = 32;

static mut conv: pam_conv = pam_conv {
    conv: Some(misc_conv),
    appdata_ptr: 0 as *const c_void as *mut c_void,
};

static mut audit_fd: libc::c_int = -1;
// static mut pamh: *mut pam_handle_t = 0 as *const pam_handle_t as *mut pam_handle_t;
static mut pamh: *mut pam_handle_t = std::ptr::null_mut();

extern "C" fn default_conv(
    num_msg: c_int,
    msg: *const *const pam_message,
    resp: *mut *mut pam_response,
    appdata_ptr: *mut c_void,
) -> c_int {
    0
}

#[derive(Copy, Clone)]
#[repr(C)]
pub struct pam_handle;

#[derive(Copy, Clone)]
#[repr(C)]
pub struct pam_message {
    pub msg_style: c_int,
    pub msg: *const c_char,
}

#[derive(Copy, Clone)]
#[repr(C)]
pub struct pam_response {
    pub resp: *mut c_char,
    pub resp_retcode: c_int,
}
#[derive(Copy, Clone)]
#[repr(C)] // Ensures that the struct has the same memory layout as the C struct
pub struct pam_conv {
    pub conv: Option<
        unsafe extern "C" fn(
            num_msg: c_int,
            msg: *const *const pam_message,
            resp: *mut *mut pam_response,
            appdata_ptr: *mut c_void,
        ) -> c_int,
    >,
    pub appdata_ptr: *mut c_void, // Equivalent to void* in C
}

impl pam_conv {
    // Example function to set the conv function
    pub fn set_conv_func(
        &mut self,
        func: extern "C" fn(
            c_int,
            *const *const pam_message,
            *mut *mut pam_response,
            *mut c_void,
        ) -> c_int,
    ) {
        self.conv = Some(func);
    }
}
#[derive(Copy, Clone)]
#[repr(C)]
struct pam_environ {
    pub entries: c_int,         // the number of pointers available
    pub requested: c_int,       // the number of pointers used (1 <= requested <= entries)
    pub list: *mut *mut c_char, // the environment storage (a list)
}
#[derive(Copy, Clone)]
#[repr(C)]
struct pam_data {
    pub name: *mut c_char,
    pub data: *mut c_void,
    pub cleanup:
        Option<extern "C" fn(pamh: *mut pam_handle_t, data: *mut c_void, error_status: c_int)>,
    pub next: *mut pam_data,
}
#[derive(Copy, Clone)]
#[repr(C)]
enum _pam_boolean {
    PAM_FALSE = 0,
    PAM_TRUE = 1,
}
#[derive(Copy, Clone)]
#[repr(C)]
struct pam_fail_delay {
    pub set: _pam_boolean,
    pub delay: c_int,
    pub begin: time_t,
    pub delay_fn_ptr: *const c_void,
}

pub type pam_handle_t = pam_handle;

#[link(name = "selinux_utils")]
#[link(name = "audit")]
#[link(name = "util")]
#[link(name = "user")]
#[link(name = "gobject-2.0")]
#[link(name = "glib-2.0")]
#[link(name = "pam")]
#[link(name = "pam_misc")]
extern "C" {
    // C standard library functions (from stdio.h)
    pub fn audit_open() -> libc::c_int;
    pub fn __errno_location() -> *mut libc::c_int;
    pub fn audit_log_acct_message(
        audit_fd_0: libc::c_int,
        type_0: libc::c_int,
        pgname: *const libc::c_char,
        op: *const libc::c_char,
        name: *const libc::c_char,
        id: libc::c_uint,
        host: *const libc::c_char,
        addr: *const libc::c_char,
        tty: *const libc::c_char,
        result: libc::c_int,
    ) -> libc::c_int;
    pub fn strdup(_: *const libc::c_char) -> *mut libc::c_char;

    pub fn audit_log_user_avc_message(
        audit_fd: c_int,
        type_: c_int,
        message: *const c_char,
        param1: *const c_char,
        param2: *const c_char,
        param3: *const c_char,
        result: c_int,
    ) -> c_int;
    pub fn selinux_init(fd: libc::c_int);
    pub fn selinux_check_root() -> libc::c_int;
    pub fn pam_start(
        service_name: *const c_char,
        user: *const c_char,
        pam_conversation: *const pam_conv,
        pamh: *mut *mut pam_handle_t,
    ) -> c_int;
    pub fn pam_strerror(pamh: *mut pam_handle_t, errnum: c_int) -> *const c_char;
    pub fn pam_set_item(pamh: *mut pam_handle_t, item_type: c_int, item: *const c_void) -> c_int;
    pub fn pam_end(pamh: *mut pam_handle_t, pam_status: c_int) -> c_int;
    pub fn pam_chauthtok(pamh: *mut pam_handle_t, flags: c_int) -> c_int;
    pub fn misc_conv(
        num_msg: c_int,
        msgm: *const *const pam_message,
        response: *mut *mut pam_response,
        appdata_ptr: *mut c_void,
    ) -> c_int;
}

#[no_mangle]
#[allow(temporary_cstring_as_ptr)]
extern "C" fn stdin_conv(
    num_msg: c_int,
    msgm: *const *const pam_message,
    response: *mut *mut pam_response,
    appdata_ptr: *mut c_void,
) -> c_int {
    unsafe {
        // Sanity test.
        if num_msg <= 0 {
            return PAM_CONV_ERR;
        }

        // Allocate memory for the responses using a Vec.
        let mut replies: Vec<pam_response> = Vec::with_capacity(num_msg as usize);

        // Convert appdata_ptr to a Rust string (if it exists).
        let appdata_str = if !appdata_ptr.is_null() {
            // 安全地转换 appdata_ptr 为字符串，但不释放原始内存
            match CStr::from_ptr(appdata_ptr as *const libc::c_char).to_str() {
                Ok(s) => s,
                Err(_) => "",
            }
        } else {
            ""
        };

        // Each prompt elicits the same response.
        for count in 0..num_msg as usize {
            let mut reply = pam_response {
                resp_retcode: 0,
                resp: ptr::null_mut(),
            };

            if (*(*msgm.add(count))).msg_style == PAM_PROMPT_ECHO_OFF {
                // Use strdup to duplicate the appdata_str.
                reply.resp = strdup(CString::new(appdata_str).unwrap().as_ptr());
            } else {
                // Use strdup to duplicate an empty string.
                reply.resp = strdup(CString::new("").unwrap().as_ptr());
            }

            replies.push(reply);
        }

        // Set the response pointer to the vector's pointer.
        *response = replies.as_mut_ptr();

        // Prevent Rust from deallocating the vector's memory.
        std::mem::forget(replies);

        // Return success.
        PAM_SUCCESS
    }
}

fn free_cstring(ptr: *mut c_char) {
    unsafe {
        if !ptr.is_null() {
            libc::free(ptr as *mut c_void);
        }
    }
}

fn convert_to_c_char(opt_str: Option<&String>) -> *const libc::c_char {
    match opt_str {
        Some(s) => {
            let c_str = CString::new(s.as_str()).expect("CString::new failed");
            c_str.as_ptr()
        }
        None => ptr::null(), // return null pointer if None
    }
}

pub fn option_string_to_c_char(s: Option<String>) -> *const c_char {
    match s {
        Some(string) => {
            let c_string = match CString::new(string) {
                Ok(cs) => cs,
                Err(_) => {
                    eprintln!("Warning: String contains null bytes, returning null pointer.");
                    return ptr::null();
                }
            };
            c_string.as_ptr()
        }
        None => ptr::null(),
    }
}

pub fn string_to_c_char(s: String) -> *const c_char {
    match CString::new(s) {
        Ok(c_string) => c_string.as_ptr(),
        Err(_) => {
            eprintln!("Warning: String contains null bytes, returning null pointer.");
            ptr::null()
        }
    }
}

#[allow(clippy::redundant_closure)]
fn option_to_c_char_safe(opt: Option<String>) -> Result<*const c_char, NulError> {
    opt.map(|s| CString::new(s))
        .transpose()? // 处理可能的 NulError
        .map(|cstr| cstr.into_raw() as *const c_char)
        .ok_or_else(|| CString::new("").unwrap_err()) // 永远不会触发
}
#[allow(clippy::missing_safety_doc)]
pub unsafe fn c_char_to_string(ptr: *const c_char) -> Result<String, std::str::Utf8Error> {
    assert!(!ptr.is_null(), "Pointer must not be null");
    let c_str = CStr::from_ptr(ptr);
    c_str.to_str().map(String::from)
}

#[allow(clippy::missing_safety_doc)]
pub unsafe fn free_c_string(ptr: *const c_char) {
    if !ptr.is_null() {
        drop(CString::from_raw(ptr as *mut c_char));
    }
}
#[allow(
    clippy::map_clone,
    clippy::comparison_to_empty,
    temporary_cstring_as_ptr
)]

const USAGE_STR: &str = "utpasswd [-kdluefS?] [-k|--keep-tokens] [-d|--delete] [-l|--lock] [-u|--unlock] [-e|--expire] [-f|--force] [-x|--maximum=DAYS] [-n|--minimum=DAYS] [-w|--warning=DAYS] [-i|--inactive=DAYS] [-S|--status]
        [--stdin] [-?|--help] [--usage] [OPTION...] <accountName>";

fn build_command() -> Command {
    Command::new("passwd")
        .disable_version_flag(true)
        .disable_help_flag(true)
        .after_help("For more information, see the passwd(1) manual page.")
        .arg(Arg::new("keep-tokens")
            .short('k')
            .long("keep-tokens")
            .help("keep non-expired authentication tokens")
            .action(clap::ArgAction::SetTrue))
        .arg(Arg::new("delete")
            .short('d')
            .long("delete")
            .help("delete the password for the named account (root only); also removes password lock if any")
            .action(clap::ArgAction::SetTrue))
        .arg(Arg::new("lock")
            .short('l')
            .long("lock")
            .help("lock the password for the named account (root only)")
            .action(clap::ArgAction::SetTrue))
        .arg(Arg::new("unlock")
            .short('u')
            .long("unlock")
            .help("unlock the password for the named account (root only)")
            .action(clap::ArgAction::SetTrue))
        .arg(Arg::new("expire")
            .short('e')
            .long("expire")
            .help("expire the password for the named account (root only)")
            .action(clap::ArgAction::SetTrue))
        .arg(Arg::new("force")
            .short('f')
            .long("force")
            .help("force operation")
            .action(clap::ArgAction::SetTrue))
        .arg(Arg::new("maximum")
            .short('x')
            .long("maximum")
            .help("maximum password lifetime (root only)")
            .value_name("DAYS")
            .value_parser(clap::value_parser!(i64)))
        .arg(Arg::new("minimum")
            .short('n')
            .long("minimum")
            .help("minimum password lifetime (root only)")
            .value_name("DAYS")
            .value_parser(clap::value_parser!(i64)))
        .arg(Arg::new("warning")
            .short('w')
            .long("warning")
            .help("number of days warning users receives before password expiration (root only)")
            .value_name("DAYS")
            .value_parser(clap::value_parser!(i64)))
        .arg(Arg::new("inactive")
            .short('i')
            .long("inactive")
            .help("number of days after password expiration when an account becomes disabled (root only)")
            .value_name("DAYS")
            .value_parser(clap::value_parser!(i64)))
        .arg(Arg::new("status")
            .short('S')
            .long("status")
            .help("report password status on the named account (root only)")
            .action(clap::ArgAction::SetTrue))
        .arg(Arg::new("stdin")
            .long("stdin")
            .help("read new tokens from stdin (root only)")
            .action(clap::ArgAction::SetTrue))
        .arg(Arg::new("help")
            .short('?')
            .long("help")
            .help("display this help and exit")
            .action(clap::ArgAction::SetTrue))
        .arg(Arg::new("usage")
            .long("usage")
            .help("Display usage information")
            .action(clap::ArgAction::SetTrue))
        .override_usage(USAGE_STR)
}

pub fn parse_args(args: &[String], min: &mut i64, max: &mut i64, warn: &mut i64, inact: &mut i64) {
    *min = -2;
    *max = -2;
    *warn = -2;
    *inact = -2;

    // 检查参数值，同时处理负数
    let mut skip_next = false;
    for (i, arg) in args.iter().enumerate() {
        if skip_next {
            skip_next = false;
            continue;
        }

        // 判断是否是需要值的选项
        let is_value_option = match arg.as_str() {
            "-w" | "--warning" | "-x" | "--maximum" | "-n" | "--minimum" | "-i" | "--inactive" => {
                true
            }
            _ => false,
        };

        if is_value_option {
            if i + 1 >= args.len() {
                eprintln!(
                    "{}: bad argument {}: missing argument",
                    get_progname_value().unwrap_or("utpasswd".to_string()),
                    arg
                );
                exit(1);
            }

            // 获取下一个参数
            let next_arg = &args[i + 1];

            // 检查是否是合法的数值（包括负数）
            if let Ok(_) = next_arg.parse::<i64>() {
                skip_next = true;
                continue;
            } else if next_arg.starts_with('-')
                && !next_arg.chars().nth(1).unwrap_or('x').is_numeric()
            {
                eprintln!(
                    "{}: bad argument {}: missing argument",
                    get_progname_value().unwrap_or("utpasswd".to_string()),
                    arg
                );
                exit(1);
            }
            skip_next = true;
        }
    }

    // 检查未知参数（排除已知的值和它们的参数）
    let mut i = 0;
    while i < args.len() {
        let arg = &args[i];
        if arg.starts_with('-') && arg != "-" {
            let is_known = match arg.as_str() {
                "-k" | "--keep-tokens" | "-d" | "--delete" | "-l" | "--lock" | "-u"
                | "--unlock" | "-e" | "--expire" | "-f" | "--force" | "-S" | "--status"
                | "--stdin" | "-?" | "--help" | "--usage" => true,
                "-w" | "--warning" | "-x" | "--maximum" | "-n" | "--minimum" | "-i"
                | "--inactive" => {
                    i += 1; // 跳过下一个参数，因为它是值
                    true
                }
                _ => false,
            };

            if !is_known && !arg.parse::<i64>().is_ok() {
                eprintln!(
                    "{}: bad argument {}: unknown option",
                    get_progname_value().unwrap_or("utpasswd".to_string()),
                    arg
                );
                exit(1);
            }
        }
        i += 1;
    } //end

    let matches = Command::new("passwd")
        .disable_version_flag(true)
        .disable_help_flag(true)
        .arg(Arg::new("keep-tokens")
            .short('k')
            .long("keep-tokens")
            .help("keep non-expired authentication tokens")
            .action(clap::ArgAction::SetTrue))
        .arg(Arg::new("delete")
            .short('d')
            .long("delete")
            .help("delete the password for the named account (root only); also removes password lock if any")
            .action(clap::ArgAction::SetTrue))
        .arg(Arg::new("lock")
            .short('l')
            .long("lock")
            .help("lock the password for the named account (root only)")
            .action(clap::ArgAction::SetTrue))
        .arg(Arg::new("unlock")
            .short('u')
            .long("unlock")
            .help("unlock the password for the named account (root only)")
            .action(clap::ArgAction::SetTrue))
        .arg(Arg::new("expire")
            .short('e')
            .long("expire")
            .help("expire the password for the named account (root only)")
            .action(clap::ArgAction::SetTrue))
        .arg(Arg::new("force")
            .short('f')
            .long("force")
            .help("force operation")
            .action(clap::ArgAction::SetTrue))
        .arg(Arg::new("maximum")
            .short('x')
            .long("maximum")
            .help("maximum password lifetime (root only)")
            .value_name("DAYS")
            .value_parser(clap::value_parser!(i64))
            .allow_negative_numbers(true))  // 允许负数
        .arg(Arg::new("minimum")
            .short('n')
            .long("minimum")
            .help("minimum password lifetime (root only)")
            .value_name("DAYS")
            .value_parser(clap::value_parser!(i64))
            .allow_negative_numbers(true))  // 允许负数
        .arg(Arg::new("warning")
            .short('w')
            .long("warning")
            .help("number of days warning users receives before password expiration (root only)")
            .value_name("DAYS")
            .value_parser(clap::value_parser!(i64))
            .allow_negative_numbers(true))  // 允许负数
        .arg(Arg::new("inactive")
            .short('i')
            .long("inactive")
            .help("number of days after password expiration when an account becomes disabled (root only)")
            .value_name("DAYS")
            .value_parser(clap::value_parser!(i64))
            .allow_negative_numbers(true))  // 允许负数
        .arg(Arg::new("status")
            .short('S')
            .long("status")
            .help("report password status on the named account (root only)")
            .action(clap::ArgAction::SetTrue))
        .arg(Arg::new("stdin")
            .long("stdin")
            .help("read new tokens from stdin (root only)")
            .action(clap::ArgAction::SetTrue))
        .arg(Arg::new("help")
            .short('?')
            .long("help")
            .help("display this help and exit")
            .action(clap::ArgAction::SetTrue))
        .arg(Arg::new("usage")
            .long("usage")
            .help("Display usage information")
            .action(clap::ArgAction::SetTrue))
        .arg(Arg::new("other")
            .num_args(0..)
            .allow_hyphen_values(false)
            .value_terminator("--")
            .help("other options"))
        .try_get_matches_from(args)
        .unwrap_or_else(|e| {
            // 处理clap的错误，转换为传统passwd风格的错误信息
            let error_msg = e.to_string();
            if error_msg.contains("missing argument") {
                // 提取参数名
                for arg in args {
                    if arg == "-w" || arg == "--warning" {
                        eprintln!("{}: bad argument {}: missing argument", get_progname_value().unwrap_or("utpasswd".to_string()), arg);
                        exit(1);
                    }
                    if arg == "-x" || arg == "--maximum" {
                        eprintln!("{}: bad argument {}: missing argument", get_progname_value().unwrap_or("utpasswd".to_string()), arg);
                        exit(1);
                    }
                    if arg == "-n" || arg == "--minimum" {
                        eprintln!("{}: bad argument {}: missing argument", get_progname_value().unwrap_or("utpasswd".to_string()), arg);
                        exit(1);
                    }
                    if arg == "-i" || arg == "--inactive" {
                        eprintln!("{}: bad argument {}: missing argument", get_progname_value().unwrap_or("utpasswd".to_string()), arg);
                        exit(1);
                    }
                }
            }
            e.exit();
        });

    if matches.get_flag("help") {
        let mut cmd = build_command();
        println!("{}", cmd.render_help());
        exit(0);
    }
    if matches.get_flag("usage") {
        let mut cmd = build_command();
        println!("{}", cmd.render_usage());
        exit(0);
    }

    set_passwd_flags_value(Some(0));

    if matches.get_flag("keep-tokens") {
        set_passwd_flags_value(Some(get_passwd_flags_value().unwrap_or(0) | PASSWD_KEEP));
    }
    if matches.get_flag("lock") {
        set_passwd_flags_value(Some(get_passwd_flags_value().unwrap_or(0) | PASSWD_LOCK));
    }
    if matches.get_flag("unlock") {
        set_passwd_flags_value(Some(get_passwd_flags_value().unwrap_or(0) | PASSWD_UNLOCK));
    }
    if matches.get_flag("expire") {
        set_passwd_flags_value(Some(get_passwd_flags_value().unwrap_or(0) | PASSWD_EXPIRE));
    }
    if matches.get_flag("status") {
        set_passwd_flags_value(Some(get_passwd_flags_value().unwrap_or(0) | PASSWD_STATUS));
    }
    if matches.get_flag("delete") {
        set_passwd_flags_value(Some(get_passwd_flags_value().unwrap_or(0) | PASSWD_DELETE));
    }
    if matches.get_flag("force") {
        set_passwd_flags_value(Some(get_passwd_flags_value().unwrap_or(0) | PASSWD_FORCE));
    }
    if matches.get_flag("stdin") {
        set_passwd_flags_value(Some(get_passwd_flags_value().unwrap_or(0) | PASSWD_STDIN));
    }

    if let Some(v) = matches.get_one::<i64>("minimum") {
        *min = *v;
        set_passwd_flags_value(Some(get_passwd_flags_value().unwrap_or(0) | PASSWD_MIN));
    }
    if let Some(v) = matches.get_one::<i64>("maximum") {
        *max = *v;
        set_passwd_flags_value(Some(get_passwd_flags_value().unwrap_or(0) | PASSWD_MAX));
    }
    if let Some(v) = matches.get_one::<i64>("warning") {
        *warn = *v;
        set_passwd_flags_value(Some(get_passwd_flags_value().unwrap_or(0) | PASSWD_WARN));
    }
    if let Some(v) = matches.get_one::<i64>("inactive") {
        *inact = *v;
        set_passwd_flags_value(Some(get_passwd_flags_value().unwrap_or(0) | PASSWD_INACT));
    }

    if let Some(flags) = get_passwd_flags_value() {
        if flags != 0 {
            let mut tmp = flags & PASSWD_ROOT;
            let mut count = 0;
            while tmp != 0 {
                if tmp & 0x01 != 0 {
                    count += 1;
                }
                tmp >>= 1;
            }
            if count > 1 {
                eprintln!(
                    "{}: Only one of -l, -u, -d, -S may be specified.",
                    get_progname_value().unwrap()
                );
                exit(-2);
            }
            if count > 0 {
                tmp = flags & PASSWD_AGING;
                if tmp != 0 {
                    eprintln!(
                        "{}: Cannot mix one of -l, -u, -d, -S and one of -i, -n, -w, -x.",
                        get_progname_value().unwrap()
                    );
                    exit(-2);
                }
            }
        }
    }

    if get_passwd_flags_value().unwrap_or(0) & !PASSWD_KEEP != 0 && getuid() != 0.into() {
        eprintln!("Only root can do that.");
        exit(-2);
    }

    set_username_value(None);
    let extra_args: Vec<String> = matches
        .get_many::<String>("other")
        .map(|vals| vals.map(|s| s.to_string()).collect())
        .unwrap_or_else(Vec::new);

    // Filter out option arguments (those starting with -)
    let extra_args: Vec<String> = extra_args
        .into_iter()
        .filter(|arg| !arg.starts_with('-'))
        .collect();

    if !extra_args.is_empty() {
        if getuid() != 0.into() {
            eprintln!(
                "{}: Only root can specify a user name.",
                get_progname_value().unwrap_or("passwd".to_string())
            );
            exit(-3);
        } else {
            set_username_value(Some(extra_args[0].clone()));
            if get_username_value().unwrap().len() > MAX_USERNAMESIZE {
                eprintln!(
                    "{}: The user name supplied is too long.",
                    get_progname_value().unwrap_or("passwd".to_string())
                );
                exit(-3);
            }
        }
        // if extra_args.len() > 1 {
        if extra_args.get(1).is_some() {
            eprintln!(
                "{}: Only one user name may be specified.",
                get_progname_value().unwrap_or("passwd".to_string())
            );
            exit(-3);
        }
        // }
    }

    if get_passwd_flags_value().unwrap_or(0) & !PASSWD_KEEP != 0
        && get_username_value().as_deref().unwrap_or("").is_empty()
    {
        eprintln!(
            "{}: This option requires a user name.",
            get_progname_value().unwrap_or("passwd".to_string())
        );
        exit(-2);
    }

    let mut pw: *mut passwd = std::ptr::null_mut();
    if get_username_value().is_none() {
        unsafe {
            pw = getpwuid(getuid().into());
        }
        if pw.is_null() {
            eprintln!(
                "{}: Can not identify you!",
                get_progname_value().unwrap_or("passwd".to_string())
            );
            exit(-3);
        }
        unsafe {
            let username_cstr = CStr::from_ptr((*pw).pw_name);
            let username_str = username_cstr.to_string_lossy().to_string();
            set_username_value(Some(username_str));
        }
    } else {
        unsafe {
            let c_username = CString::new(get_username_value().unwrap()).unwrap();
            pw = getpwnam(c_username.as_ptr());
        }
        if pw.is_null() {
            eprintln!(
                "{}: Unknown user name '{}'.",
                get_progname_value().unwrap_or("passwd".to_string()),
                get_username_value().unwrap()
            );
            exit(-4);
        }
    }
}

fn print_c_char_ptr(ptr: *mut c_char) {
    unsafe {
        if ptr.is_null() {
            eprintln!("Null pointer");
            return;
        }

        let c_str = CStr::from_ptr(ptr);
        let rust_str = c_str.to_string_lossy();
        eprintln!("{}", rust_str);
    }
}

unsafe fn is_recoverable_error() -> bool {
    // Check if errno is one of the expected recoverable errors
    let err = *__errno_location();
    err == EINVAL || err == EPROTONOSUPPORT || err == EAFNOSUPPORT
}

#[allow(unused_variables, clippy::manual_strip, temporary_cstring_as_ptr)]
fn main() {
    // Handle co
    // Define the variables
    let mut retval: libc::c_int = 0; // Equivalent to int retval;
    let mut min: i64 = 0; // Equivalent to long min;
    let mut max: i64 = 0; // Equivalent to long max;
    let mut warn: i64 = 0; // Equivalent to long warn;
    let mut inact: i64 = 0; // Equivalent to long inact;
                            // let mut pamh: *mut pam_handle_t = 0 as *const pam_handle_t as *mut pam_handle_t;
                            // let mut pamh: *mut pam_handle_t = ptr::null_mut();

    // Define a raw pointer for struct passwd
    let mut pwd: *mut passwd = std::ptr::null_mut();

    // Define raw pointers for tty_name and ttyn (C strings)
    let mut tty_name: *mut c_char = std::ptr::null_mut();
    let mut ttyn: *mut c_char = std::ptr::null_mut();

    unsafe {
        audit_fd = audit_open();

        // Check if audit_open failed
        if audit_fd < 0 && !is_recoverable_error() {
            eprintln!("Error - unable to connect to audit system");
            process::exit(1);
        }
    }
    // 获取命令行参数
    let args: Vec<String> = env::args().collect();

    // set_progname_value(Some(args[0].clone()));
    set_progname_value(Some(
        Path::new(&args[0])
            .file_name()
            .and_then(|s| s.to_str())
            .unwrap_or(&args[0])
            .to_string(),
    ));

    parse_args(&args, &mut min, &mut max, &mut warn, &mut inact);

    unsafe {
        let username_cstr = match get_username_value() {
            Some(ref user) => CString::new(user.as_str()).expect("CString::new failed"), // If Some, convert to CString
            None => {
                eprintln!("Username is None.");
                process::exit(-4);
            }
        };
        //eprintln!(
        //    "{:?} :Changing password for {:?}.",
        //    get_progname_value().unwrap(),
        //    username_cstr
        //);
        // Now you can use `username_cstr` safely
        // println!("Username as CString: {:?}", username_cstr);

        let pwd = getpwnam(username_cstr.as_ptr());
        if pwd.is_null() {
            eprintln!(
                " '{}' Unknown user name '{}'.",
                get_progname_value().unwrap(),
                get_username_value().unwrap()
            );
            process::exit(-4);
        } else {
            // 在这里可以使用 pwd 指针访问用户信息
            // 例如：(*pwd).pw_uid, (*pwd).pw_gid, etc.
            //println!("User found!");
        }
        // eprintln!("----------pwd = {:?}.", pwd);

        selinux_init(audit_fd);

        // Check if the SELinux is denying access
        if selinux_check_root() != 0 {
            eprintln!(
                "{}: SELinux denying access due to security policy.",
                get_progname_value().unwrap()
            );

            // Logging the message to audit (pseudo function call)
            // Assuming audit_log_acct_message is a valid function in your context
            audit_log_acct_message(
                audit_fd,
                AUDIT_USER_CHAUTHTOK,
                ptr::null(),
                CString::new("attempted-to-change-password")
                    .expect("CString::new failed")
                    .as_ptr(),
                ptr::null(),
                (*pwd).pw_uid,
                ptr::null(),
                ptr::null(),
                ptr::null(),
                0,
            );
            process::exit(1);
        }

        if get_passwd_flags_value().unwrap() & PASSWD_LOCK != 0 {
            eprintln!(
                "Locking password for user  {}",
                username_cstr.to_string_lossy()
            );
            retval =
                libuser::pwdb_lock_password(option_to_c_char_safe(get_username_value()).unwrap());
            if retval != 0 {
                eprintln!(
                    "{} : Error (password not set?)",
                    get_progname_value().unwrap(),
                );
            } else {
                eprintln!("{} : Success", get_progname_value().unwrap());
            }
            audit_log_acct_message(
                audit_fd,
                AUDIT_ACCT_LOCK,
                ptr::null(),
                CString::new("locked-password")
                    .expect("CString::new failed")
                    .as_ptr(),
                ptr::null(),
                (*pwd).pw_uid,
                ptr::null(),
                ptr::null(),
                ptr::null(),
                if retval == 0 { 1 } else { 0 },
            );
            process::exit(1);
        }
        if get_passwd_flags_value().unwrap() & PASSWD_UNLOCK != 0 {
            eprintln!(
                "Unlocking password for user  {}",
                username_cstr.to_string_lossy()
            );
            retval = libuser::pwdb_unlock_password(
                option_to_c_char_safe(get_username_value()).unwrap(),
                get_passwd_flags_value().unwrap() & PASSWD_FORCE,
            );
            if retval == 0 {
                eprintln!("{} : Success", get_progname_value().unwrap());
            } else if retval == -2 {
                eprintln!(
                    "{} : Unsafe operation (use -f to force)",
                    get_progname_value().unwrap(),
                );
            } else {
                eprintln!(
                    "{} : Error (password not set?)",
                    get_progname_value().unwrap()
                );
            }
            audit_log_acct_message(
                audit_fd,
                AUDIT_ACCT_UNLOCK,
                ptr::null(),
                CString::new("unlocked-password")
                    .expect("CString::new failed")
                    .as_ptr(),
                ptr::null(),
                (*pwd).pw_uid,
                ptr::null(),
                ptr::null(),
                ptr::null(),
                if retval == 0 { 1 } else { 0 },
            );
            process::exit(1);
        }

        if get_passwd_flags_value().unwrap() & PASSWD_EXPIRE != 0 {
            eprintln!(
                "Expiring password for user  {}",
                username_cstr.to_string_lossy()
            );
            retval = libuser::pwdb_update_aging(
                option_to_c_char_safe(get_username_value()).unwrap(),
                -2,
                -2,
                -2,
                -2,
                0,
            );
            if retval == 0 {
                eprintln!("{} : Success", get_progname_value().unwrap());
            } else {
                eprintln!("{} : Error", get_progname_value().unwrap());
            }
            audit_log_acct_message(
                audit_fd,
                AUDIT_USER_MGMT,
                ptr::null(),
                CString::new("expired-password")
                    .expect("CString::new failed")
                    .as_ptr(),
                ptr::null(),
                (*pwd).pw_uid,
                ptr::null(),
                ptr::null(),
                ptr::null(),
                if retval == 0 { 1 } else { 0 },
            );
            process::exit(1);
        }

        if get_passwd_flags_value().unwrap() & PASSWD_DELETE != 0 {
            eprintln!(
                "Removing password for user  {}",
                username_cstr.to_string_lossy()
            );
            retval =
                libuser::pwdb_clear_password(option_to_c_char_safe(get_username_value()).unwrap());
            if retval == 0 {
                eprintln!("{} : Success", get_progname_value().unwrap());
            } else {
                eprintln!("{} : Error", get_progname_value().unwrap());
            }
            audit_log_acct_message(
                audit_fd,
                AUDIT_USER_CHAUTHTOK,
                ptr::null(),
                CString::new("expired-password")
                    .expect("CString::new failed")
                    .as_ptr(),
                ptr::null(),
                (*pwd).pw_uid,
                ptr::null(),
                ptr::null(),
                ptr::null(),
                if retval == 0 { 1 } else { 0 },
            );
            process::exit(1);
        }

        if get_passwd_flags_value().unwrap() & PASSWD_STATUS != 0 {
            retval =
                libuser::pwdb_display_status(option_to_c_char_safe(get_username_value()).unwrap());
            if retval == 0 {
                //eprintln!("{} : Success", get_progname_value().unwrap());
            } else {
                eprintln!("{} : Error", get_progname_value().unwrap());
            }
            process::exit(1);
        }

        if get_passwd_flags_value().unwrap() & PASSWD_AGING != 0 {
            let mut aubuf: [c_char; PATH_MAX as usize] = [0; PATH_MAX as usize];
            eprintln!(
                "Adjusting password for user  {}",
                username_cstr.to_string_lossy()
            );
            retval = libuser::pwdb_update_aging(
                option_to_c_char_safe(get_username_value()).unwrap(),
                min as libc::c_long,
                max as libc::c_long,
                warn as libc::c_long,
                inact as libc::c_long,
                -2 as libc::c_long,
            );
            if retval == 0 {
                eprintln!("{} : Success", get_progname_value().unwrap());
            } else {
                eprintln!("{} : Error", get_progname_value().unwrap());
            }
            let formatted_string = format!(
                "changed-password-aging min={} max={} warn={} inact={}",
                min, max, warn, inact
            );
            let formatted_bytes = formatted_string.as_bytes();
            let copy_len = std::cmp::min(formatted_bytes.len(), PATH_MAX as usize - 1);
            for i in 0..copy_len {
                aubuf[i] = formatted_bytes[i] as c_char;
            }
            aubuf[copy_len] = 0;

            audit_log_acct_message(
                audit_fd,
                AUDIT_USER_MGMT,
                ptr::null(),
                aubuf.as_ptr(),
                ptr::null(),
                (*pwd).pw_uid,
                ptr::null(),
                ptr::null(),
                ptr::null(),
                if retval == 0 { 1 } else { 0 },
            );
            process::exit(1);
        }
        eprintln!(
            "Changing password for user {}",
            username_cstr.to_string_lossy()
        );

        if get_passwd_flags_value().unwrap() & PASSWD_STDIN != 0 {
            let mut new_password = vec![0u8; PAM_MAX_RESP_SIZE]; // Use a vector for dynamic size
                                                                 // Read from stdin
                                                                 // let mut new_password = get_new_password();
            let mut stdin = io::stdin();
            let bytes_read = match stdin.read(&mut new_password) {
                Ok(bytes) => bytes,
                Err(e) => {
                    eprintln!(
                        "{}: error reading from stdin: {}",
                        get_progname_value().unwrap(),
                        e
                    );
                    process::exit(1);
                }
            };
            // eprintln!("Changing password for user1111111 ");
            // Check if the read exceeds the buffer size (PAM_MAX_RESP_SIZE)
            if bytes_read == PAM_MAX_RESP_SIZE {
                // Check if the last character is a newline
                if new_password[bytes_read - 1] != b'\n' {
                    eprintln!(
                        "{}: password too long, maximum is {}",
                        get_progname_value().unwrap(),
                        PAM_MAX_RESP_SIZE - 1
                    );
                    process::exit(1);
                }
            }

            // Null-terminate the string
            let mut password_string = String::from_utf8_lossy(&new_password[..bytes_read])
                .to_string()
                .trim_end_matches('\n')
                .to_string();

            // Find the position of the newline character
            if let Some(pos) = password_string.find('\n') {
                // Remove the newline character by truncating the string at the found position
                password_string.truncate(pos);
            }
            // Print the password for testing (you can remove this in a production scenario)
            // println!("Password is: {}", password_string);

            conv.conv = Some(stdin_conv);
            // let c_str = CString::new(password_string).unwrap();
            set_new_password(Some(password_string));
            // Allocate memory for appdata_ptr (mimicking strdup behavior in Rust)
            // conv.appdata_ptr = c_str.into_raw() as *mut c_void; // Store the raw pointer for FFI
            // Assuming `get_new_password()` returns an `Option<String>`
            let password_option: Option<String> = get_new_password();

            // Convert the password to a raw pointer
            let raw_ptr: *mut c_void = match password_option {
                Some(password) => {
                    // Convert String to CString
                    let c_string =
                        CString::new(password).expect("Failed to convert String to CString");
                    // Get a raw pointer and leak it (to avoid deallocation)
                    c_string.into_raw() as *mut c_void
                }
                None => std::ptr::null_mut(), // Handle the None case
            };

            conv.appdata_ptr = raw_ptr; // Store the raw pointer for FFI
                                        // println!("appdata_ptr: {:?}", conv.appdata_ptr);
        }

        let current_username = option_to_c_char_safe(get_username_value()).unwrap();

        retval = pam_start(
            CString::new("utpasswd").unwrap().as_ptr() as *const c_char,
            current_username,
            addr_of!(conv),
            &mut pamh,
        );

        if retval != PAM_SUCCESS {
            let err_msg = {
                let err_ptr = pam_strerror(pamh, retval);
                // CString::from_raw(err_ptr as *mut c_char)
                //     .into_string()
                //     .unwrap_or_else(|_| "Unknown error".to_string())
                c_char_to_string(err_ptr).unwrap()
            };
            eprintln!(
                "{}: unable to start pam: {}",
                get_progname_value().unwrap(),
                err_msg
            );
            exit(1);
        }

        // eprintln!("Changing password for user33333 retval = {}", retval);
        // Handle tty
        ttyn = libc::ttyname(0); // Get the tty name for stdin (file descriptor 0)
                                 // eprintln!("Changing password for user3333344444 ttyn =");
                                 // print_c_char_ptr(ttyn);
        if !ttyn.is_null() {
            let mut ttyn_str = std::ffi::CStr::from_ptr(ttyn).to_str().unwrap();
            if ttyn_str.starts_with("/dev/") {
                tty_name = CString::new(ttyn_str[5..].to_string()).unwrap().into_raw();
            // Skip "/dev/" prefix
            } else {
                tty_name = CString::new(ttyn_str).unwrap().into_raw();
            }
            // eprintln!("Changing password for user44444 tty_name = ");
            // print_c_char_ptr(tty_name);
            retval = pam_set_item(pamh, PAM_TTY as libc::c_int, tty_name as *const c_void);
            // eprintln!("Changing password for user4444455555 retval = {}", retval);
            if retval != PAM_SUCCESS {
                let err_msg = {
                    let err_ptr = pam_strerror(pamh, retval);
                    CString::from_raw(err_ptr as *mut c_char)
                        .into_string()
                        .unwrap_or_else(|_| "Unknown error".to_string())
                };
                eprintln!(
                    "{}: unable to set tty for pam: {}",
                    get_progname_value().unwrap(),
                    err_msg
                );
                pam_end(pamh, retval);
                exit(1);
            }
        }

        // eprintln!("Changing password for user777777777 retval = ");
        // Change authentication token if needed
        retval = pam_chauthtok(
            pamh,
            if get_passwd_flags_value().unwrap() & PASSWD_KEEP != 0 {
                PAM_CHANGE_EXPIRED_AUTHTOK as libc::c_int
            } else {
                0 as libc::c_int
            },
        );
        // eprintln!(
        //     "Changing password for user555 retval = {} PAM_SUCCESS = {}   bool= {}",
        //     retval,
        //     PAM_SUCCESS,
        //     get_passwd_flags_value().unwrap() & PASSWD_KEEP
        // );

        if retval == PAM_SUCCESS {
            // Successfully updated authentication token
            retval = pam_end(pamh, PAM_SUCCESS);
            if get_passwd_flags_value().unwrap() & PASSWD_KEEP != 0 {
                println!(
                    "{}: expired authentication tokens updated successfully.",
                    get_progname_value().unwrap()
                );
            } else {
                println!(
                    "{}: all authentication tokens updated successfully.",
                    get_progname_value().unwrap()
                );
            }
            retval = 0;
        } else {
            // Error occurred during token update
            let err_msg = {
                let err_ptr = pam_strerror(pamh, retval);
                c_char_to_string(err_ptr).unwrap()
            };
            eprintln!("{}: {}", get_progname_value().unwrap(), err_msg);
            pam_end(pamh, retval);
            retval = 1;
        }
    }
}

#[cfg(test)]
mod tests {
    #![allow(clippy::unnecessary_cast)]
    use super::*;
    // use clap::{Arg, Command};
    use libc::passwd;
    use libc::EACCES;
    use std::ffi::{CStr, CString};
    use std::os::raw::{c_int, c_void};
    // use std::process;
    use std::ptr;

    pub const PAM_TEXT_INFO: c_int = 4;

    #[test]
    fn test_stdin_conv_with_zero_messages() {
        let result = stdin_conv(0, ptr::null(), ptr::null_mut(), ptr::null_mut());
        assert_eq!(result, PAM_CONV_ERR);
    }

    #[test]
    fn test_stdin_conv_with_null_appdata() {
        let cstr = CString::new("test").unwrap();
        let msg = pam_message {
            msg_style: PAM_PROMPT_ECHO_OFF as i32,
            msg: cstr.as_ptr(),
        };
        let msg_ptr = &msg as *const pam_message;
        let msg_arr = &msg_ptr as *const *const pam_message;

        let mut response: *mut pam_response = ptr::null_mut();

        let result = stdin_conv(
            1,
            msg_arr,
            &mut response as *mut *mut pam_response,
            ptr::null_mut(),
        );
        assert_eq!(result, PAM_SUCCESS);

        unsafe {
            assert!(!(*response).resp.is_null());
            let resp_str = CStr::from_ptr((*response).resp).to_str().unwrap();
            assert_eq!(resp_str, "");
        }
    }

    #[test]
    fn test_stdin_conv_with_appdata() {
        let cstr = CString::new("test").unwrap();
        let msg = pam_message {
            msg_style: PAM_PROMPT_ECHO_OFF as i32,
            msg: cstr.as_ptr(),
        };
        let msg_ptr = &msg as *const pam_message;
        let msg_arr = &msg_ptr as *const *const pam_message;

        let mut response: *mut pam_response = ptr::null_mut();
        let appdata = CString::new("password").unwrap();

        let result = stdin_conv(
            1,
            msg_arr,
            &mut response as *mut *mut pam_response,
            appdata.as_ptr() as *mut c_void,
        );
        assert_eq!(result, PAM_SUCCESS);

        unsafe {
            assert!(!(*response).resp.is_null());
            let resp_str = CStr::from_ptr((*response).resp).to_str().unwrap();
            assert_eq!(resp_str, "password");
        }
    }

    #[test]
    fn test_stdin_conv_with_non_echo_off() {
        let cstr = CString::new("test").unwrap();
        let msg = pam_message {
            msg_style: PAM_TEXT_INFO as i32,
            msg: cstr.as_ptr(),
        };
        let msg_ptr = &msg as *const pam_message;
        let msg_arr = &msg_ptr as *const *const pam_message;

        let mut response: *mut pam_response = ptr::null_mut();

        let result = stdin_conv(
            1,
            msg_arr,
            &mut response as *mut *mut pam_response,
            ptr::null_mut(),
        );
        assert_eq!(result, PAM_SUCCESS);

        unsafe {
            assert!(!(*response).resp.is_null());
            let resp_str = CStr::from_ptr((*response).resp).to_str().unwrap();
            assert_eq!(resp_str, "");
        }
    }

    #[test]
    fn test_stdin_conv_with_multiple_messages() {
        let cstr1 = CString::new("test1").unwrap();
        let cstr2 = CString::new("test2").unwrap();
        let msg1 = pam_message {
            msg_style: PAM_PROMPT_ECHO_OFF as i32,
            msg: cstr1.as_ptr(),
        };
        let msg2 = pam_message {
            msg_style: PAM_TEXT_INFO as i32,
            msg: cstr2.as_ptr(),
        };

        let msg_ptrs = [&msg1 as *const pam_message, &msg2 as *const pam_message];
        let msg_arr = msg_ptrs.as_ptr();

        let mut response: *mut pam_response = ptr::null_mut();
        let appdata = CString::new("password").unwrap();

        let result = stdin_conv(
            2,
            msg_arr,
            &mut response as *mut *mut pam_response,
            appdata.as_ptr() as *mut c_void,
        );
        assert_eq!(result, PAM_SUCCESS);

        unsafe {
            let resp1 = CStr::from_ptr((*response.add(0)).resp).to_str().unwrap();
            assert_eq!(resp1, "password");

            let resp2 = CStr::from_ptr((*response.add(1)).resp).to_str().unwrap();
            assert_eq!(resp2, "");
        }
    }

    #[test]
    fn test_convert_to_c_char_with_some() {
        let input = String::from("test string");
        let result = convert_to_c_char(Some(&input));

        unsafe {
            assert!(!result.is_null());
            let c_str = CStr::from_ptr(result);
            assert_eq!(c_str.to_str().unwrap(), "test string");
        }
    }

    #[test]
    fn test_convert_to_c_char_with_none() {
        let result = convert_to_c_char(None);
        assert!(result.is_null());
    }

    #[test]
    fn test_convert_to_c_char_with_empty_string() {
        let input = String::from("");
        let result = convert_to_c_char(Some(&input));

        unsafe {
            assert!(!result.is_null());
            let c_str = CStr::from_ptr(result);
            assert_eq!(c_str.to_str().unwrap(), "");
        }
    }

    #[test]
    #[allow(clippy::unnecessary_literal_unwrap)]
    fn test_option_string_to_c_char_with_some() {
        let input = Some(String::from("测试字符串"));
        let result = string_to_c_char(input.unwrap());

        unsafe {
            assert!(!result.is_null());
            let c_str = CStr::from_ptr(result);
            assert_eq!(c_str.to_str().unwrap(), "测试字符串");
        }
    }

    #[test]
    fn test_option_string_to_c_char_with_none() {
        let result = option_string_to_c_char(None);
        assert!(result.is_null());
    }

    #[test]
    fn test_option_string_to_c_char_with_empty_string() {
        let input = Some(String::from(""));
        let result = option_string_to_c_char(input);

        unsafe {
            assert!(!result.is_null());
            let c_str = CStr::from_ptr(result);
            assert_eq!(c_str.to_str().unwrap(), "");
        }
    }

    // Mock functions for testing
    fn mock_getuid() -> u32 {
        0
    }
    fn mock_getpwuid(_uid: u32) -> *mut passwd {
        unsafe {
            static mut MOCK_PW: passwd = passwd {
                pw_name: b"testuser\0".as_ptr() as *mut i8,
                pw_passwd: b"x\0".as_ptr() as *mut i8,
                pw_uid: 1000,
                pw_gid: 1000,
                pw_gecos: b"Test User\0".as_ptr() as *mut i8,
                pw_dir: b"/home/testuser\0".as_ptr() as *mut i8,
                pw_shell: b"/bin/bash\0".as_ptr() as *mut i8,
            };
            &mut MOCK_PW
        }
    }
    fn mock_getpwnam(_name: *const i8) -> *mut passwd {
        mock_getpwuid(0)
    }

    #[test]
    fn test_parse_args_with_min_max_values() {
        let args = vec![
            "passwd".to_string(),
            "-n".to_string(),
            "5".to_string(),
            "-x".to_string(),
            "90".to_string(),
            "-w".to_string(),
            "7".to_string(),
            "-i".to_string(),
            "30".to_string(),
        ];

        let mut min = -2;
        let mut max = -2;
        let mut warn = -2;
        let mut inact = -2;

        parse_args(&args, &mut min, &mut max, &mut warn, &mut inact);

        assert_eq!(min, 5);
        assert_eq!(max, 90);
        assert_eq!(warn, 7);
        assert_eq!(inact, 30);
    }

    #[test]
    fn test_parse_args_with_flags() {
        let args = vec![
            "passwd".to_string(),
            "-l".to_string(),
            "-f".to_string(),
            "testuser".to_string(),
        ];

        let mut min = -2;
        let mut max = -2;
        let mut warn = -2;
        let mut inact = -2;

        parse_args(&args, &mut min, &mut max, &mut warn, &mut inact);

        assert!(get_passwd_flags_value().unwrap() & PASSWD_LOCK != 0);
        assert!(get_passwd_flags_value().unwrap() & PASSWD_FORCE != 0);
        assert_eq!(get_username_value().unwrap(), "testuser");
    }

    #[test]
    #[should_panic(expected = "Only root can do that")]
    fn test_parse_args_non_root_with_privileged_flags() {
        // Override getuid for this test
        fn mock_getuid_non_root() -> u32 {
            1000
        }

        let args = vec![
            "passwd".to_string(),
            "-d".to_string(),
            "testuser".to_string(),
        ];

        let mut min = -2;
        let mut max = -2;
        let mut warn = -2;
        let mut inact = -2;

        parse_args(&args, &mut min, &mut max, &mut warn, &mut inact);
    }

    #[test]
    fn test_parse_args_with_username() {
        let args = vec!["passwd".to_string(), "testuser".to_string()];

        let mut min = -2;
        let mut max = -2;
        let mut warn = -2;
        let mut inact = -2;

        parse_args(&args, &mut min, &mut max, &mut warn, &mut inact);

        assert_eq!(get_username_value().unwrap(), "testuser");
    }

    #[test]
    fn test_parse_args_with_keep_tokens() {
        let args = vec!["passwd".to_string(), "-k".to_string()];

        let mut min = -2;
        let mut max = -2;
        let mut warn = -2;
        let mut inact = -2;

        parse_args(&args, &mut min, &mut max, &mut warn, &mut inact);

        assert!(get_passwd_flags_value().unwrap() & PASSWD_KEEP != 0);
    }

    #[test]
    fn test_print_c_char_ptr_with_valid_string() {
        let c_string = CString::new("测试字符串").unwrap();
        let ptr = c_string.into_raw();

        // Create a pipe to capture stderr
        let (read_fd, write_fd) = unsafe {
            let mut fds = [0; 2];
            libc::pipe(fds.as_mut_ptr());
            (fds[0], fds[1])
        };

        // Redirect stderr to our pipe
        unsafe {
            let stderr_fd = libc::dup(libc::STDERR_FILENO);
            libc::dup2(write_fd, libc::STDERR_FILENO);

            print_c_char_ptr(ptr);

            // Restore original stderr
            libc::dup2(stderr_fd, libc::STDERR_FILENO);
            libc::close(stderr_fd);
            libc::close(write_fd);
        }

        // Read from pipe
        let mut buffer = [0u8; 1024];
        let bytes_read = unsafe {
            libc::read(
                read_fd,
                buffer.as_mut_ptr() as *mut libc::c_void,
                buffer.len(),
            )
        };
        unsafe { libc::close(read_fd) };

        // Verify output
        if bytes_read > 0 {
            let output = String::from_utf8_lossy(&buffer[..bytes_read as usize]);
            assert!(output.contains("测试字符串"));
        } else {
            panic!("No output captured");
        }

        // Clean up
        unsafe {
            let _ = CString::from_raw(ptr);
        };
    }

    #[test]
    fn test_print_c_char_ptr_with_null_pointer() {
        // Similar setup as above test but checking for "Null pointer" output
        let (read_fd, write_fd) = unsafe {
            let mut fds = [0; 2];
            libc::pipe(fds.as_mut_ptr());
            (fds[0], fds[1])
        };

        unsafe {
            let stderr_fd = libc::dup(libc::STDERR_FILENO);
            libc::dup2(write_fd, libc::STDERR_FILENO);

            print_c_char_ptr(std::ptr::null_mut());

            libc::dup2(stderr_fd, libc::STDERR_FILENO);
            libc::close(stderr_fd);
            libc::close(write_fd);
        }

        let mut buffer = [0u8; 1024];
        let bytes_read = unsafe {
            libc::read(
                read_fd,
                buffer.as_mut_ptr() as *mut libc::c_void,
                buffer.len(),
            )
        };
        unsafe { libc::close(read_fd) };

        if bytes_read > 0 {
            let output = String::from_utf8_lossy(&buffer[..bytes_read as usize]);
            assert!(output.contains("Null pointer"));
        } else {
            panic!("No output captured");
        }
    }

    #[test]
    fn test_print_c_char_ptr_with_empty_string() {
        let c_string = CString::new("").unwrap();
        let ptr = c_string.into_raw();

        // Create a pipe to capture stderr
        let (read_fd, write_fd) = unsafe {
            let mut fds = [0; 2];
            libc::pipe(fds.as_mut_ptr());
            (fds[0], fds[1])
        };

        // Redirect stderr to our pipe
        unsafe {
            let stderr_fd = libc::dup(libc::STDERR_FILENO);
            libc::dup2(write_fd, libc::STDERR_FILENO);

            print_c_char_ptr(ptr);

            // Restore original stderr
            libc::dup2(stderr_fd, libc::STDERR_FILENO);
            libc::close(stderr_fd);
            libc::close(write_fd);
        }

        // Read from pipe
        let mut buffer = [0u8; 1024];
        let bytes_read = unsafe {
            libc::read(
                read_fd,
                buffer.as_mut_ptr() as *mut libc::c_void,
                buffer.len(),
            )
        };
        unsafe { libc::close(read_fd) };

        // Verify no "Null pointer" message was printed
        if bytes_read > 0 {
            let output = String::from_utf8_lossy(&buffer[..bytes_read as usize]);
            assert!(!output.contains("Null pointer"));
        }

        // Clean up
        unsafe {
            let _ = CString::from_raw(ptr);
        };
    }
    unsafe fn set_errno(err: i32) {
        *__errno_location() = err;
    }

    #[test]
    fn test_is_recoverable_error_with_einval() {
        unsafe {
            set_errno(EINVAL);
            assert!(is_recoverable_error());
        }
    }

    #[test]
    fn test_is_recoverable_error_with_eprotonosupport() {
        unsafe {
            set_errno(EPROTONOSUPPORT);
            assert!(is_recoverable_error());
        }
    }

    #[test]
    fn test_is_recoverable_error_with_eafnosupport() {
        unsafe {
            set_errno(EAFNOSUPPORT);
            assert!(is_recoverable_error());
        }
    }

    #[test]
    fn test_is_recoverable_error_with_non_recoverable_error() {
        unsafe {
            set_errno(EACCES); // 不可恢复的错误
            assert!(!is_recoverable_error());
        }
    }

    #[test]
    fn test_is_recoverable_error_with_zero() {
        unsafe {
            set_errno(0); // 无错误
            assert!(!is_recoverable_error());
        }
    }
}
